﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Design;
using System.Diagnostics;
using System.Globalization;
using System.Runtime.InteropServices;
using EnvDTE;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;

namespace VarunRobinson.DocumentHelper_vsix
{
    /// <summary>
    /// This is the class that implements the package exposed by this assembly.
    ///
    /// The minimum requirement for a class to be considered a valid package for Visual Studio is to
    /// implement the IVsPackage interface and register itself with the shell. This package uses the
    /// helper classes defined inside the Managed Package Framework (MPF) to do it: it derives from
    /// the Package class that provides the implementation of the IVsPackage interface and uses the
    /// registration attributes defined in the framework to register itself and its components with
    /// the shell.
    /// </summary>
    // This attribute tells the PkgDef creation utility (CreatePkgDef.exe) that this class is
    // a package.
    [PackageRegistration(UseManagedResourcesOnly = true)]
    // This attribute is used to register the information needed to show this package in the
    // Help/About dialog of Visual Studio.
    [InstalledProductRegistration("#110", "#112", "1.0", IconResourceID = 400)]
    // This attribute is needed to let the shell know that this package exposes some menus.
    [ProvideMenuResource("Menus.ctmenu", 1)]
    [Guid(GuidList.guidDocumentHelper_vsixPkgString)]
    [ProvideAutoLoad(UIContextGuids80.SolutionExists)]
    public sealed class DocumentHelper_vsixPackage : Package
    {
        /// <summary>
        /// Default constructor of the package. Inside this method you can place any initialization
        /// code that does not require any Visual Studio service because at this point the package
        /// object is created but not sited yet inside Visual Studio environment. The place to do
        /// all the other initialization is the Initialize method.
        /// </summary>
        public DocumentHelper_vsixPackage()
        {
            Debug.WriteLine(string.Format(CultureInfo.CurrentCulture, "Entering constructor for: {0}", this.ToString()));
        }

        /////////////////////////////////////////////////////////////////////////////
        // Overridden Package Implementation

        #region Package Members

        /// <summary>
        /// Initialization of the package; this method is called right after the package is sited,
        /// so this is the place where you can put all the initialization code that rely on services
        /// provided by VisualStudio.
        /// </summary>
        protected override void Initialize()
        {
            Debug.WriteLine(string.Format(CultureInfo.CurrentCulture, "Entering Initialize() of: {0}", this.ToString()));
            base.Initialize();

            // Add our command handlers for menu (commands must exist in the .vsct file)
            OleMenuCommandService mcs = GetService(typeof(IMenuCommandService)) as OleMenuCommandService;
            if (null != mcs)
            {
                // Create the command for the menu item.
                //CommandID menuCommandID = new CommandID(GuidList.guidDocumentHelper_vsixCmdSet, (int)PkgCmdIDList.cmdidDocumentActiveWindow);
                //OleMenuCommand menuItemthisFile = new OleMenuCommand(DocumentThisFile, menuCommandID);
                //mcs.AddCommand(menuItemthisFile);

                CommandID menuCommandID = new CommandID(GuidList.guidDocumentHelper_vsixCmdSet, (int)PkgCmdIDList.cmdidDocumentThisFile);
                OleMenuCommand menuItemthisFile = new OleMenuCommand(DocumentThisFile, menuCommandID);
                menuItemthisFile.BeforeQueryStatus +=menuItemthisFile_BeforeQueryStatus;
                mcs.AddCommand(menuItemthisFile);

                menuCommandID = new CommandID(GuidList.guidDocumentHelper_vsixCmdSet, (int)PkgCmdIDList.cmdidDocumentThisClass);
                OleMenuCommand menuItemThisClass = new OleMenuCommand(DocumentThisClass, menuCommandID);
                menuItemThisClass.BeforeQueryStatus += menuItemThisClass_BeforeQueryStatus;
                mcs.AddCommand(menuItemThisClass);

                menuCommandID = new CommandID(GuidList.guidDocumentHelper_vsixCmdSet, (int)PkgCmdIDList.cmdidDocumentThisFunction);
                OleMenuCommand olemenuItem = new OleMenuCommand(DocumentThisFunction, menuCommandID);
                olemenuItem.BeforeQueryStatus += olemenuItem_BeforeQueryStatus;        
                mcs.AddCommand(olemenuItem);
            }
        }

        private void menuItemthisFile_BeforeQueryStatus(object sender, EventArgs e)
        {
            OleMenuCommand menuCommand = sender as OleMenuCommand;
            if (menuCommand != null)
            {
                DTE dte = GetService(typeof(DTE)) as DTE;
                Document doc = dte.ActiveDocument;
                if (doc != null)
                {
                    var file = doc.ProjectItem.FileCodeModel;
                    if (file == null)
                    {
                        menuCommand.Visible = false;
                    }
                    else
                    {
                        menuCommand.Visible = true;
                    }
                }
                else
                {
                    menuCommand.Visible = false;
                }
            }
        }

        private void menuItemThisClass_BeforeQueryStatus(object sender, EventArgs e)
        {
             OleMenuCommand menuCommand = sender as OleMenuCommand;
             if (menuCommand != null)
             {
                 DTE dte = GetService(typeof(DTE)) as DTE;

                 TextSelection sel = (TextSelection)dte.ActiveDocument.Selection;
                 TextPoint pnt = (TextPoint)sel.ActivePoint;

                 CodeElement elem = pnt.get_CodeElement(vsCMElement.vsCMElementClass);

                 if (elem == null)
                 {
                     menuCommand.Visible = false;

                 }
                 else
                 {
                     menuCommand.Visible = true;
                 }
             }
        }

        private void olemenuItem_BeforeQueryStatus(object sender, EventArgs e)
        {
            OleMenuCommand menuCommand = sender as OleMenuCommand;
            if (menuCommand != null)
            {

                DTE dte = GetService(typeof(DTE)) as DTE;

                TextSelection sel = (TextSelection)dte.ActiveDocument.Selection;
                TextPoint pnt = (TextPoint)sel.ActivePoint;

                CodeElement elem = pnt.get_CodeElement(vsCMElement.vsCMElementFunction);

                if (elem == null)
                {
                    menuCommand.Visible = false;
                   
                }
                else
                {
                    menuCommand.Visible = true;
                }
            }
        }

        #endregion Package Members


        private void DocumentThisFile(object sender, EventArgs e)
        {


            DTE dte = GetService(typeof(DTE)) as DTE;
            Document doc = dte.ActiveDocument;
            if (doc != null)
            {
                var file = doc.ProjectItem.FileCodeModel;
                DocumentFile(file);
            }
        }

        private void DocumentThisClass(object sender, EventArgs e)
        {


            DTE dte = GetService(typeof(DTE)) as DTE;
          
            TextSelection sel = (TextSelection)dte.ActiveDocument.Selection;
            TextPoint pnt = (TextPoint)sel.ActivePoint;

            CodeElement elem = pnt.get_CodeElement(vsCMElement.vsCMElementClass);

            if (elem != null)
            {
                CodeClass ct = elem as CodeClass;
                DocumentClass(ct);
            }
        }

        private void DocumentThisFunction(object sender, EventArgs e)
        {


            DTE dte = GetService(typeof(DTE)) as DTE;

            TextSelection sel = (TextSelection)dte.ActiveDocument.Selection;
            TextPoint pnt = (TextPoint)sel.ActivePoint;

            CodeElement elem = pnt.get_CodeElement(vsCMElement.vsCMElementFunction);

            if (elem != null)
            {
                CodeFunction cf = elem as CodeFunction;
                if (cf != null)
                {
                    DocumentFunction(cf);
                }
            }
        }

        #region Helper Functions

        private static void DocumentFile(FileCodeModel file)
        {
            IEnumerable<CodeElement> codeElements = GetCodeElementsRecursively(file);
            foreach (var ce in codeElements)
            {
                if (ce.Kind == vsCMElement.vsCMElementClass)
                {
                    CodeClass ct = ce as CodeClass;
                    DocumentClass(ct);
                }
            }
        }

        private static void DocumentClass(CodeClass ct)
        {
            if (ct != null)
            {
                foreach (var member in ct.Members)
                {
                    //Get functions
                    CodeFunction cf = member as CodeFunction;
                    if (cf != null)
                    {
                        DocumentFunction(cf);
                    }
                }

                MyDocClass docForCodeClass = new MyDocClass();
                docForCodeClass.Summary = ct.Name;
                ct.DocComment = docForCodeClass.DocumentClass();
            }
        }

        private static void DocumentFunction(CodeFunction cf)
        {
            EditPoint start = cf.StartPoint.CreateEditPoint();
            FileCodeModel fcm = cf.ProjectItem.FileCodeModel;
            do
            {
               

                    var mytext = start.GetText(start.LineLength);

                    var exceptions = mytext.Contains("throw");

                start.LineDown();

            } while (start.LessThan(cf.EndPoint));


            if (cf.DocComment == "<doc>\r\n</doc>")
            {
                var docClass = new MyDocClass();
                docClass.Summary = cf.Name;
                List<string> myparams = new List<string>();
                foreach (var p in cf.Parameters)
                {
                    CodeParameter cp = p as CodeParameter;
                    if (cp != null)
                    {
                        myparams.Add(cp.Name);
                    }
                }
                docClass.Parameters = myparams;
                if (cf.Type != null)
                {
                    docClass.Returns = cf.Type.AsFullName;
                }
                cf.DocComment = docClass.DocumentFunction();
            }
        }

        /// <summary></summary>
        /// <param name="model"></param>
        /// <returns></returns>
        private static IEnumerable<CodeElement> GetCodeElementsRecursively(FileCodeModel model)
        {
            var ret = new List<CodeElement>();
            if (model != null)
            {
                if (model.CodeElements == null)
                    return ret;
                foreach (CodeElement ce in model.CodeElements)
                {
                    ret.Add(ce);
                    ret.AddRange(GetCodeElementsRecursively(ce));
                }
            }
            return ret;
        }

        private static IEnumerable<CodeElement> GetCodeElementsRecursively(CodeElement codeElement)
        {
            var ret = new List<CodeElement>();

            CodeNamespace cns = codeElement as CodeNamespace;

            if (cns == null)
                return ret;
            foreach (CodeElement ce in cns.Members)
            {
                ret.Add(ce);
                ret.AddRange(GetCodeElementsRecursively(ce));
            }
            return ret;
        }

        #endregion Helper Functions
    }
}